package com.googlecode.lightest.core

import java.lang.reflect.Method

import org.testng.annotations.*
import org.testng.ITestContext
import org.testng.SuiteResult

class LightestTestCase extends GroovyTestCase {
    IDispatcherAssignmentStrategy strategy
    IDomainSpecificApi api
    ITaskDispatcher dispatcher

    def builder

    LightestTestCase() {
        builder = new TaskNodeBuilder()
    }

    /**
     * Assigns this test class instance to a task dispatcher, with its
     * associated listener and environment.
     * 
     * Makes the association between this class and the its test environment
     * available to the listener. We perform the mapping indirectly through the
     * listener because the TestNG interfaces offer no facility for performing
     * the association directly.
     */
    @BeforeClass
    void assign(ITestContext context) {
        strategy = context.getAttribute(LightestTestListener.ATTR_STRATEGY)
        strategy.assign(this)
        
        def listener = dispatcher.getListener()
        
        listener.addEnvironmentMapping(this.class, dispatcher.getEnvironment())
    }
    
    @AfterClass
    void unassign() {
        strategy.unassign(this)
    }

    /**
     * Here we tamper with the TestNG suite internals so the suite result is
     * available for intermediate report generation. We are bad!!!
     */
    @AfterMethod
    void updateReport(ITestContext context) {
        synchronized (context) {
            def suite = context.getSuite()
            def suiteResult = new SuiteResult(suite.m_suite, context)
    
            // the suite runner will safely overwrite this when it wants to
            suite.m_suiteResults.put(context.getName(), suiteResult)
        }
    }
    
    /**
     * Clear the local environment settings.
     */
    @AfterMethod
    void cleanEnvironment() {
        dispatcher.getEnvironment().clearLocal()
    }

    /**
     * In order to enable any domain specific API's in this testcase, the API
     * should be set with this method. One technique is to subclass this class,
     * set the API in a @BeforeTest method, and derive test classes from that
     * subclass.
     *
     * @param api
     */
    void setApi(IDomainSpecificApi api) {
        this.api = api
    }

    /**
     * The ITaskDispatcher is injected into the testcase.
     *
     * @param dispatcher
     */
    void setDispatcher(ITaskDispatcher dispatcher) {
        this.dispatcher = dispatcher
    }

    IDomainSpecificApi getApi() {
        return api
    }
    /**
     * Returns the currently assigned task dispatcher, or null..
     */
    ITaskDispatcher getDispatcher() {
        return dispatcher
    }

    /**
     * Attempts to treat calls to missing methods as API task invocations.
     *
     * Task invocations may be specified with a builder-like nested syntax. If
     * specified in this way, they will be invoked in depth-first order, with
     * the results of the parent tasks available to be referenced by the child
     * tasks via the task result hierarchy. The configuration of parent tasks
     * is also available to child tasks.
     *
     * If a parent task fails, the child tasks will not be performed. However,
     * sibling tasks will be performed.
     */
    def methodMissing(String name, args) {
        LightestUtils.setBuilderAsDelegate(args, builder)

        def taskTree = builder.invokeMethod(name, args)

        dispatcher.dispatch(taskTree)
    }
}
